package com.imooc.business.core.executor.query.db;


import com.google.common.collect.Maps;
import com.imooc.business.builder.CollectSqlBuilder;
import com.imooc.business.constants.QueryConstant;
import com.imooc.business.constants.SymbolConstants;
import com.imooc.business.context.DataContext;
import com.imooc.business.domain.Storage;
import com.imooc.business.util.DateUtil;
import com.imooc.infra.repository.jdbc.params.StandardSqlParam;
import com.imooc.module.collect.enums.DimensionEnum;
import com.imooc.module.collect.enums.PeriodEnum;
import com.imooc.module.query.request.QueryRequest;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;

import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;


public class BaseQueryDb {

    private static final Integer PAGE_SIZE = 200;
    private static final Integer OFFSET = 0;

    /**
     * 构建查询sql
     * @param ruleEnName
     * @param signList
     * @param queryRequest
     * @return
     */
    public String buildQuerySql(String ruleEnName, Set<String> signList, QueryRequest queryRequest) {
        DimensionEnum DimensionEnum = queryRequest.getDimension();
        PeriodEnum period = queryRequest.getPeriodType();
        Integer pageSize = getPageSize(queryRequest.getPageSize()) ;
        Integer offset = getOffset(queryRequest.getOffset());

        //按照每日 | 特定时间范围的， 需要自己去统计
        boolean groupFlag = period.getType() == PeriodEnum.DAY.getType() || period.getType() == PeriodEnum.CUSTOM.getType();
        String queryColumn = "";
        if (groupFlag){
            queryColumn = signList.stream()
                    .distinct().map(sign -> "sum(" + sign + ") as " + sign).collect(Collectors.joining(SymbolConstants.COMMA));
        } else {
            queryColumn = StringUtils.join(signList, SymbolConstants.COMMA);
        }

        //获取表名
        String tableName = Storage.getDefaultTableName(ruleEnName, DimensionEnum, period);

        String dimensionColumn = StandardSqlParam.getDimensionColumn(DimensionEnum);

        StringBuilder sqlBuilder = new StringBuilder();
        sqlBuilder.append("SELECT ").append(dimensionColumn).append(", ").append(queryColumn).append(" FROM  ").append(tableName)
         .append(" WHERE ").append(CollectSqlBuilder.buildDimensionCodition(DimensionEnum));
        if (groupFlag){
            sqlBuilder.append(" AND "+ StandardSqlParam.COUNT_TIME +" >= :").append(StandardSqlParam.START_TIME).
                    append(" AND "+StandardSqlParam.COUNT_TIME+" <= :").append(StandardSqlParam.END_TIME);
        } else {
            sqlBuilder.append(" AND "+StandardSqlParam.COUNT_TIME+" =:").append(StandardSqlParam.COUNT_TIME);
        }
        if (groupFlag){
            sqlBuilder.append(" GROUP BY ").append(dimensionColumn);
        }

        sqlBuilder.append(" limit ").append(pageSize).append(" offset ").append(offset);

        return sqlBuilder.toString();
    }

    /**
     * 构建图表查询sql
     * @param ruleEnName
     * @param signList
     * @param queryRequest
     * @param pattern
     * @return
     */
    public String buildChartQuerySql(String ruleEnName, Set<String> signList, QueryRequest queryRequest, String pattern) {
        PeriodEnum period = queryRequest.getPeriodType();
        DimensionEnum DimensionEnum = queryRequest.getDimension();
        String queryColumn = signList.stream()
                .distinct().map(sign -> "sum(" + sign + ") as " + sign).collect(Collectors.joining(SymbolConstants.COMMA));

        //获取表名
        String tableName = Storage.getDefaultTableName(ruleEnName, DimensionEnum, period);

        StringBuilder sqlBuilder = new StringBuilder();
        sqlBuilder.append("SELECT left( "+StandardSqlParam.COUNT_TIME+", '").append(pattern.length()).append("' ) as ").append(QueryConstant.DATE).
                append(", ").append(queryColumn).append(" FROM  ").append(tableName)
                .append(" WHERE ").append(CollectSqlBuilder.buildDimensionCodition(DimensionEnum));
        sqlBuilder.append(" AND "+StandardSqlParam.COUNT_TIME+" >= :").append(StandardSqlParam.START_TIME).
                    append(" AND "+StandardSqlParam.COUNT_TIME+" <= :").append(StandardSqlParam.END_TIME);
        sqlBuilder.append(" GROUP BY left( "+StandardSqlParam.COUNT_TIME+", '").append(pattern.length()).append("' ) ");
        sqlBuilder.append("order by "+ QueryConstant.DATE);
        return sqlBuilder.toString();
    }


    /**
     * 构建查询参数
     * @param queryRequest
     * @return
     */
    public Map<String, Object> buildParams(QueryRequest queryRequest) {
        Map<String, Object> params = Maps.newHashMap();
        List<Long> userIds = queryRequest.getUserIds();
        List<String> groupIds = queryRequest.getGroupIds();
        List<Long> schoolIds = queryRequest.getSchoolIds();
        PeriodEnum periodType = queryRequest.getPeriodType();
        if (CollectionUtils.isNotEmpty(userIds)) {
            params.put(StandardSqlParam.USER_IDS, userIds);
            if (userIds.size() == 1) {
                params.put(StandardSqlParam.USER_ID, userIds.get(0));
            }
        }
        if (CollectionUtils.isNotEmpty(groupIds)) {
            params.put(StandardSqlParam.GROUP_ID, groupIds);
        }
        if (CollectionUtils.isNotEmpty(schoolIds)) {
            params.put(StandardSqlParam.SCHOOL_ID, schoolIds);
        }
        String startDate = DateUtil.formatDate(queryRequest.getStartTime(), StandardSqlParam.DATE_PATTERN);
        String endDate = DateUtil.formatDate(queryRequest.getEndTime(), StandardSqlParam.DATE_PATTERN);

        boolean groupFlag = periodType.getType() == PeriodEnum.DAY.getType() || periodType.getType() == PeriodEnum.CUSTOM.getType();
        if (!groupFlag){
            params.put(StandardSqlParam.COUNT_TIME, DataContext.buildCountTime(periodType, queryRequest.getStartTime(), queryRequest.getEndTime()));
        }
        params.put(StandardSqlParam.START_TIME, startDate);
        params.put(StandardSqlParam.END_TIME, endDate);
        return params;
    }

    public String getDataSource(){
        return Storage.DEFAULT_DATASOURCE;
    }

    private Integer getPageSize(Integer pageSize){
        return Optional.ofNullable(pageSize).orElse(PAGE_SIZE);
    }

    private Integer getOffset(Integer offset){
        return Optional.ofNullable(offset).orElse(OFFSET);
    }

}
